﻿#include "FITKGraphObjectSketchInteractivePreviewer.h"

// C++
#include <cmath>

// VTK
#include <vtkUnstructuredGrid.h>
#include <vtkPoints.h>
#include <vtkProperty.h>
#include <vtkVector.h>
#include <vtkMath.h>
#include <vtkTransform.h>
#include <vtkParametricSpline.h>
#include <vtkParametricFunctionSource.h>
#include <vtkLine.h>

// Graph
#include "FITKGraphCommons.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"

// Render
#include "FITK_Component/FITKRenderWindowVTK/FITKGraphRender.h"

#include <QDebug>

namespace Exchange
{
    FITKGraphObjectSketchInteractivePreviewer::FITKGraphObjectSketchInteractivePreviewer(AbaqusData::FITKDataCase* caseData)
        : FITKGraphObject3D(caseData, nullptr)
    {
        // Initialize.
        init();

        // Generate the actor.
        generateGraph();

        // Set the layer need to be rendered.
        setRenderLayer(1);

        // Save if the bounds are fixed or dynamic.
        m_hasFixedBounds = false;
    }

    FITKGraphObjectSketchInteractivePreviewer::~FITKGraphObjectSketchInteractivePreviewer()
    {
        // Delete the vtk dataset.
        deleteVtkObj(m_ugrid);
        deleteVtkObj(m_points);
    }

    void FITKGraphObjectSketchInteractivePreviewer::init()
    {
        // Create the grid lines.
        m_ugrid = vtkUnstructuredGrid::New();
        m_points = vtkPoints::New();
        m_ugrid->SetPoints(m_points);
    }

    void FITKGraphObjectSketchInteractivePreviewer::updateVisibility()
    {
        return;
    }

    void FITKGraphObjectSketchInteractivePreviewer::update(bool forceUpdate)
    {
        Q_UNUSED(forceUpdate);

        // Try to modeling.
        modeling();
    }

    bool FITKGraphObjectSketchInteractivePreviewer::getDataVisibility()
    {
        return true;
    }

    void FITKGraphObjectSketchInteractivePreviewer::addPressedPosition(double* pos)
    {
        if (!m_fActor->GetVisibility())
        {
            return;
        }

        // Check the new position by shape type.
        switch (m_shapeType)
        {
        case SST_Vertex:
        case SST_ConHLine:
        case SST_ConVLine:
            break;
        case SST_Wire:
        case SST_Spline:
        case SST_ArcOfTan:
        {
            // Check whether the last position and the new position are the same.
            if (m_pressedPoses.count() != 0)
            {
                if (m_pressedPoses.last() == vtkVector3d(pos[0], pos[1], pos[2]))
                {
                    return;
                }
            }

            break;
        }
        case SST_ArcOfP3:
        case SST_Ellipse:
        {
            vtkVector3d newPos(pos[0], pos[1], pos[2]);

            // Check whether the position has been added.
            if (m_pressedPoses.contains(newPos))
            {
                return;
            }

            // Check if the three points are in the same line.
            if (m_pressedPoses.count() == 2)
            {
                vtkVector3d v1;
                vtkVector3d v2;
                for (int i = 0; i < 3; i++)
                {
                    v1[i] = m_pressedPoses[1][i] - m_pressedPoses[0][i];
                    v2[i] = newPos[i] - m_pressedPoses[1][i];
                }

                vtkVector3d newVec = v1.Cross(v2);
                if (qFuzzyCompare(0, newVec.Norm()))
                {
                    return;
                }
            }

            break;
        }
        case SST_Rect:
        case SST_Circle:
        case SST_ArcOfCircle:
        case SST_ConLine:
        case SST_ConCircle:
        {
            // Check whether the position has been added.
            if (m_pressedPoses.contains(vtkVector3d(pos[0], pos[1], pos[2])))
            {
                return;
            }

            break;
        }
        default:
            return;
        }

        // Clear the previewed positions.
        m_previewedPoses.clear();

        // Add the position to the list.
        m_pressedPoses.push_back(vtkVector3d(pos[0], pos[1], pos[2]));

        // Emit the signal for GUI.
        if (!m_pressedPoses.isEmpty())
        {
            vtkVector3d posLast = m_pressedPoses.last();
            QVector<double> poses;
            poses.push_back(posLast[0]);
            poses.push_back(posLast[1]);
            poses.push_back(posLast[2]);
            emit sig_validPressed(poses);
        }

        // Try to make shape.
        modeling();       
    }

    void FITKGraphObjectSketchInteractivePreviewer::addPreviewedPosition(double* pos)
    {
        if (!m_fActor->GetVisibility())
        {
            return;
        }

        if (m_shapeType == SketchShapeType::SST_NONE)
        {
            return;
        }

        // Clear the previewed positions.
        m_previewedPoses.clear();

        // Add the position to the list.
        m_previewedPoses.push_back(vtkVector3d(pos[0], pos[1], pos[2]));

        // Try to make shape.
        modeling();
    }

    void FITKGraphObjectSketchInteractivePreviewer::removeLastPressedPosition()
    {
        if (m_pressedPoses.count() == 0)
        {
            return;
        }

        switch (m_shapeType)
        {
        case SST_Wire:
        {
            // 0 position.
            if (!m_val.isValid())
            {
                return;
            }

            // Only the first segment can be undoed.
            QVector<double> savedPos = m_val.value<QVector<double>>();
            vtkVector3d firstPos = m_pressedPoses.first();
            if (!(FITKGraphCommons::FuzzyCompare(firstPos[0], savedPos[0]) 
                && FITKGraphCommons::FuzzyCompare(firstPos[1], savedPos[1])
                && FITKGraphCommons::FuzzyCompare(firstPos[2], savedPos[2])))
            {
                return;
            }

            break;
        }
        case SST_Rect:
        case SST_Circle:
        case SST_Ellipse:
        case SST_ArcOfTan:
        case SST_ArcOfCircle:
        case SST_ArcOfP3:
        case SST_Spline:
        case SST_ConLine:
        case SST_ConCircle:
            break;
        default:
            return;
        }

        // Remove the last position.
        m_pressedPoses.removeLast();

        // Try to clear the cache.
        m_val = QVariant();

        // Try to make shape.
        modeling();
    }

    void FITKGraphObjectSketchInteractivePreviewer::setShapeType(SketchShapeType type, QVariant param)
    {
        if (m_shapeType == type && m_shapeParam == param)
        {
            return;
        }

        m_shapeType = type;
        m_shapeParam = param;

        // Clear cache.
        clearData();

        if (m_fActor)
        {
            m_fActor->SetVisibility(type != SketchShapeType::SST_NONE);
        }
    }

    void FITKGraphObjectSketchInteractivePreviewer::setPlane(double* pos, double* nor, double* up, double* right, double size)
    {
        for (int i = 0; i < 3; i++)
        {
            m_planePos[i] = pos[i];
            m_planeNor[i] = nor[i];
            m_planeUp[i] = up[i];
            m_planeRight[i] = right[i];
        }

        m_size = size;
    }

    void FITKGraphObjectSketchInteractivePreviewer::generateGraph()
    {
        double color3[3]{ 0., 0., 0. };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_sketchPreviewColor, color3);

        // Create the actor.
        m_fActor = FITKGraphActor::New();
        m_fActor->setGraphObject(this);
        m_fActor->SetPickable(false);
        m_fActor->SetVisibility(false);
        m_fActor->setScalarVisibility(false);
        m_fActor->GetProperty()->SetRepresentation(1);
        m_fActor->GetProperty()->SetColor(color3);
        m_fActor->GetProperty()->SetLineWidth(1.);
        disableLightShadow(m_fActor);
        addActor(m_fActor);

        // Initialize the actor's input.
        m_fActor->setInputDataObject(m_ugrid);
        
        // Initialize the polygon offsets.( Smaller than the sketch grid lines. )
        m_fActor->setRelativeCoincidentTopologyPolygonOffsetParameters(-1.5);
    }

    void FITKGraphObjectSketchInteractivePreviewer::disableLightShadow(FITKGraphActor* fActor)
    {
        if (!fActor)
        {
            return;
        }

        fActor->GetProperty()->SetAmbient(1.);
    }

    void FITKGraphObjectSketchInteractivePreviewer::clearData()
    {
        // Clear grid.
        resetVtkObj(m_ugrid);
        resetVtkObj(m_points);
        m_ugrid->SetPoints(m_points);

        // Clear saved positions.
        m_pressedPoses.clear();
        m_previewedPoses.clear();

        // Clear the addin parameter.
        m_val = QVariant();
        m_lastArcDeg = 0.;

        // Update the actor.
        if (m_ugrid)
        {
            m_ugrid->Modified();
        }
    }

    QVector<double> FITKGraphObjectSketchInteractivePreviewer::getPathPositions()
    {
        QList<vtkVector3d> vtkPoses = m_pressedPoses;

        // Remove the first position of the wire.( except the first segment )
        if (m_shapeType == SketchShapeType::SST_Wire)
        {
            if (m_pressedPoses.count() == 2 && m_val.isValid())
            {
                QVector<double> savedPos = m_val.value<QVector<double>>();
                if (m_pressedPoses[0] != vtkVector3d(savedPos[0], savedPos[1], savedPos[2]))
                {
                    vtkPoses.takeFirst();
                }
            }
        }

        QVector<double> poses;
        for (const vtkVector3d & pos : vtkPoses)
        {
            poses.push_back(pos[0]);
            poses.push_back(pos[1]);
            poses.push_back(pos[2]);
        }

        return poses;
    }

    void FITKGraphObjectSketchInteractivePreviewer::endSketch()
    {
        switch (m_shapeType)
        {
        case SST_Wire:
        {
            m_pressedPoses.clear();

            // Clear the cache.
            m_val = QVariant();

            break;
        }
        case SST_Spline:
        {
            QVector<double> poses = getPathPositions();

            // Emit the finish signal while the number of positions is above 2.
            if (m_pressedPoses.count() >= 2)
            {
                m_pressedPoses.clear();
                emit sig_modelingFinished(poses);
            }

            break;
        }
        default:
            break;
        }

        // Reset the sketch mode.
        // setShapeType(SketchShapeType::SST_NONE);
    }

    void FITKGraphObjectSketchInteractivePreviewer::modeling()
    {
        bool finishFlag = false;

        if (!m_ugrid || !m_points)
        {
            return;
        }

        // Reset the vtk data.
        resetVtkObj(m_ugrid);
        resetVtkObj(m_points);
        m_ugrid->SetPoints(m_points);

        // Generate the dataset.
        //@{
        switch (m_shapeType)
        {
        // Shape.
        case SST_Vertex:
            finishFlag = drawVertex();
            break;
        case SST_Wire:
            finishFlag = drawWire();
            break;
        case SST_Rect:
            finishFlag = drawRect();
            break;
        case SST_Circle:
            finishFlag = drawCircle();
            break;
        case SST_Ellipse:
            finishFlag = drawEllipse();
            break;
        case SST_ArcOfCircle:
            finishFlag = drawArcCircle();
            break;
        case SST_ArcOfTan:
            finishFlag = drawArcTan();
            break;
        case SST_ArcOfP3:
            finishFlag = drawArcThreePoints();
            break;
        case SST_Spline:
            finishFlag = drawSpline();
            break;
        // Construction.
        case SST_ConLine:
            finishFlag = drawConLine();
            break;
        case SST_ConHLine:
            finishFlag = drawConHLine();
            break;
        case SST_ConVLine:
            finishFlag = drawConVLine();
            break;
        case SST_ConAngleLine:
            finishFlag = drawConAngleLine();
            break;
        case SST_ConCircle:
            finishFlag = drawConCircle();
            break;
        default:
            return;
        }
        //@}

        // Update the pipeline.
        m_ugrid->Modified();

        if (finishFlag)
        {
            finishModeling();
        }
    }

    void FITKGraphObjectSketchInteractivePreviewer::finishModeling()
    {
        QVector<double> poses = getPathPositions();

        // Emit the finish signal.
        emit sig_modelingFinished(poses);

        // Disable previewing.
        if (m_shapeType == SketchShapeType::SST_Wire)
        {
            // Add the last position again.
            if (m_pressedPoses.count() == 0)
            {
                m_shapeType = SketchShapeType::SST_NONE;
                clearData();
            }
            else
            {
                vtkVector3d lastPos = m_pressedPoses.takeLast();

                // Clear saved positions.
                m_pressedPoses.clear();
                m_previewedPoses.clear();

                // If the first position and the last position is the same, quit modeling.
                QVector<double> savedPos = m_val.value<QVector<double>>();
                if (FITKGraphCommons::FuzzyCompare(lastPos[0], savedPos[0]) 
                    && FITKGraphCommons::FuzzyCompare(lastPos[1], savedPos[1])
                    && FITKGraphCommons::FuzzyCompare(lastPos[2], savedPos[2]))
                {
                    m_shapeType = SketchShapeType::SST_NONE;
                    clearData();
                }
                else
                {
                    m_pressedPoses.push_back(lastPos);

                    // Clear grid.
                    resetVtkObj(m_ugrid);
                    resetVtkObj(m_points);
                    m_ugrid->SetPoints(m_points);

                    // Update the actor.
                    if (m_ugrid)
                    {
                        m_ugrid->Modified();
                    }
                }
            }
        }
        else if (m_shapeType == SketchShapeType::SST_Spline)
        {
            m_shapeType = SketchShapeType::SST_NONE;
            clearData();
        }
        else
        {
            // m_shapeType = SketchShapeType::SST_NONE;
            clearData();
        }
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawVertex()
    {
        return !m_pressedPoses.isEmpty();
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawWire()
    {
        if (m_pressedPoses.isEmpty())
        {
            return false;
        }

        // Save the position before at first.
        if (!m_val.isValid())
        {
            QVector<double> posFirst;
            vtkVector3d vtkPos = m_pressedPoses.first();
            posFirst << vtkPos[0] << vtkPos[1] << vtkPos[2];
            m_val = QVariant::fromValue(posFirst);
        }

        // Add the last saved position.
        QList<vtkVector3d> poses = m_pressedPoses + m_previewedPoses;
        if (poses.count() < 2)
        {
            return false;
        }

        int nPos = poses.count();

        // Build the dataset.
        m_points->InsertNextPoint(poses[nPos - 2][0], poses[nPos - 2][1], poses[nPos - 2][2]);
        m_points->InsertNextPoint(poses[nPos - 1][0], poses[nPos - 1][1], poses[nPos - 1][2]);

        vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
        cell->InsertNextId(0);
        cell->InsertNextId(1);
        m_ugrid->InsertNextCell(VTKCellType::VTK_LINE, cell);

        bool finishFlag = m_pressedPoses.count() == 2;
        return finishFlag;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawRect()
    {
        if (m_pressedPoses.isEmpty())
        {
            return false;
        }

        QList<vtkVector3d> poses = m_pressedPoses + m_previewedPoses;
        if (poses.count() < 2)
        {
            return false;
        }

        // Get the vector for two points.
        double vec3[3]{ poses[1][0] - poses[0][0], poses[1][1] - poses[0][1], poses[1][2] - poses[0][2] };

        // Project the vector to the plane's right and up.
        double projRight[3], projUp[3];
        vtkMath::ProjectVector(vec3, m_planeRight, projRight);
        vtkMath::ProjectVector(vec3, m_planeUp, projUp);

        // Get the four positions for the rect.
        double posFirst[3]{ poses[0][0], poses[0][1], poses[0][2] };
        double posSecond[3]{ posFirst[0] + projRight[0], posFirst[1] + projRight[1], posFirst[2] + projRight[2] };
        double posThird[3]{ poses[1][0], poses[1][1], poses[1][2] };
        double posLast[3]{ poses[1][0] - projRight[0], poses[1][1] - projRight[1], poses[1][2] - projRight[2] };

        // Build the dataset.
        m_points->InsertNextPoint(posFirst);
        m_points->InsertNextPoint(posSecond);
        m_points->InsertNextPoint(posThird);
        m_points->InsertNextPoint(posLast);

        for (int i = 0; i < 4; i++)
        {
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(i);
            cell->InsertNextId((i + 1) % 4);
            m_ugrid->InsertNextCell(VTKCellType::VTK_LINE, cell);
        }

        // Pressed + Previewed
        if (m_pressedPoses.count() == 1)
        {
            return false;
        }
        // Pressed + Pressed
        else if(m_pressedPoses.count() == 2)
        {
            // Deal with the positions.
            m_pressedPoses.clear();
            m_pressedPoses.push_back(vtkVector3d(posFirst));
            m_pressedPoses.push_back(vtkVector3d(posSecond));
            m_pressedPoses.push_back(vtkVector3d(posThird));
            m_pressedPoses.push_back(vtkVector3d(posLast));         

            return true;
        }

        return false;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawCircle()
    {
        if (m_pressedPoses.isEmpty())
        {
            return false;
        }

        QList<vtkVector3d> poses = m_pressedPoses + m_previewedPoses;
        if (poses.count() < 2)
        {
            return false;
        }

        double orgPt[3]{ poses[0][0], poses[0][1], poses[0][2] };
        double outPt[3]{ poses[1][0], poses[1][1], poses[1][2] };
        vtkVector3d axis(m_planeNor);

        int numPts = 30;
        double degStep = 360. / numPts;

        vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
        transform->RotateWXYZ(degStep, axis[0], axis[1], axis[2]);
        transform->Translate(-orgPt[0], -orgPt[1], -orgPt[2]);
     
        for (int i = 0; i < numPts; i++)
        {
            double* newOutPos = transform->TransformDoublePoint(outPt);
            outPt[0] = newOutPos[0] + orgPt[0];
            outPt[1] = newOutPos[1] + orgPt[1];
            outPt[2] = newOutPos[2] + orgPt[2];
            m_points->InsertNextPoint(outPt);
        }

        for (int i = 0; i < numPts; i++)
        {
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(i);
            cell->InsertNextId((i + 1) % numPts);
            m_ugrid->InsertNextCell(VTKCellType::VTK_LINE, cell);
        }

        // Pressed + Previewed
        if (m_pressedPoses.count() == 1)
        {
            return false;
        }
        // Pressed + Pressed
        else if (m_pressedPoses.count() >= 2)
        {
            return true;
        }

        return false;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawEllipse()
    {
        QList<vtkVector3d> poses = m_pressedPoses + m_previewedPoses;
        if (poses.count() < 2)
        {
            return false;
        }

        // Get the two axis of the ellipse.
        double orgPt[3]{ poses[0][0], poses[0][1], poses[0][2] };
        double outPt[3]{ poses[1][0], poses[1][1], poses[1][2] };
        vtkVector3d vecPNor = vtkVector3d(m_planeNor).Normalized();
        vtkVector3d axis_1 = vtkVector3d(outPt[0] - orgPt[0], outPt[1] - orgPt[1], outPt[2] - orgPt[2]).Normalized();
        vtkVector3d axis_2 = vecPNor.Cross(axis_1).Normalized();

        int numPts = 30;
        double deltaThe = 2 * vtkMath::Pi() / numPts;
        double aLen = sqrt(vtkMath::Distance2BetweenPoints(orgPt, outPt));
        double bLen = 0.;

        vtkVector3d thirdPtOnAxis;

        // b = a * 0.5
        if (poses.count() == 2)
        {
            bLen = aLen * 0.5;
        }
        else if (poses.count() >= 3)
        {
            // Check if the three points are in the same line.
            vtkVector3d v1;
            vtkVector3d v2;
            for (int i = 0; i < 3; i++)
            {
                v1[i] = poses[1][i] - poses[0][i];
                v2[i] = poses[2][i] - poses[1][i];
            }

            vtkVector3d newVec = v1.Cross(v2);
            if (qFuzzyCompare(0, newVec.Norm()))
            {
                return false;
            }

            // Get the projection on the second axis.
            double outPt_2[3]{ poses[2][0], poses[2][1], poses[2][2] };
            double vecOut2[3]{ outPt_2[0] - orgPt[0], outPt_2[1] - orgPt[1], outPt_2[2] - orgPt[2] };
            double outVecProj_2[3]{ 0., 0., 0. };
            vtkMath::ProjectVector(vecOut2, axis_2.GetData(), outVecProj_2);

            // Transform.
            double newOutPt_2[3];
            for (int i = 0; i < 3; i++)
            {
                newOutPt_2[i] = outVecProj_2[i] + orgPt[i];
            }

            thirdPtOnAxis = vtkVector3d(newOutPt_2[0], newOutPt_2[1], newOutPt_2[2]);
            bLen = sqrt(vtkMath::Distance2BetweenPoints(orgPt, newOutPt_2));        
        }

        // Get the ellipse points.
        for (int i = 0; i < numPts; ++i)
        {
            double theta = deltaThe * i;

            double aScale = aLen * cos(theta);
            double bScale = bLen * sin(theta);

            vtkVector3d point(0., 0., 0.);
            for (int j = 0; j < 3; j++)
            {
                point[j] = orgPt[j] + axis_1[j] * aScale + axis_2[j] * bScale;
            }

            m_points->InsertNextPoint(point.GetData());
        }

        for (int i = 0; i < numPts; i++)
        {
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(i);
            cell->InsertNextId((i + 1) % numPts);
            m_ugrid->InsertNextCell(VTKCellType::VTK_LINE, cell);
        }

        // Pressed + Previewed / Pressed + ( Previewed )
        if (m_pressedPoses.count() == 1 || m_pressedPoses.count() == 2)
        {
            return false;
        }
        // Pressed + Pressed + Pressed
        else if (m_pressedPoses.count() >= 3)
        {
            // Deal with the last position.
            m_pressedPoses.replace(2, thirdPtOnAxis);

            // Deal with the axis.
            if (aLen < bLen)
            {
                m_pressedPoses.swapItemsAt(1, 2);
            }

            return true;
        }

        return false;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawArcCircle()
    {
        if (m_pressedPoses.isEmpty())
        {
            return false;
        }

        QList<vtkVector3d> poses = m_pressedPoses + m_previewedPoses;
        if (poses.count() < 2)
        {
            return false;
        }

        double orgPt[3]{ poses[0][0], poses[0][1], poses[0][2] };
        double outPt[3]{ poses[1][0], poses[1][1], poses[1][2] };
        vtkVector3d axis(m_planeNor);

        int numSeg = 60;
        double degBetween = 0.;
        bool closed = false;
        bool drawRadius = false;

        // Two points: Draw the full circle.
        if (poses.count() == 2)
        {   
            closed = true;
            drawRadius = false;
            degBetween = 360.;
        }
        // Three points: Try to draw the arc.
        else if (poses.count() == 3)
        {
            // The second and third positions are the same, draw the full circle.
            if (poses[1] == poses[2])
            {
                closed = true;
                drawRadius = false;
                degBetween = 360.;
            }
            // Draw the special arc with radius line.
            else if (poses[0] == poses[2])
            {
                closed = false;
                drawRadius = true;
                degBetween = 90.;
            }
            // Draw arc.
            else
            {
                closed = false;
                drawRadius = false;

                // Get the third position.
                double outPt_2[3]{ poses[2][0], poses[2][1], poses[2][2] };

                // Get the angle with the start position to the center and the mouse position to the center.
                vtkVector3d vecStart3(outPt[0] - orgPt[0], outPt[1] - orgPt[1], outPt[2] - orgPt[2]);
                vtkVector3d vecMouse3(outPt_2[0] - orgPt[0], outPt_2[1] - orgPt[1], outPt_2[2] - orgPt[2]);
                degBetween = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(vecStart3.GetData(), vecMouse3.GetData()));

                // Get the cross of two vectors.
                vtkVector3d cross = vecStart3.Cross(vecMouse3);
                double dot = vtkMath::Dot(cross.GetData(), m_planeNor);

                bool lastAntiClock = m_val.toBool();
                bool antiClock = (dot / qAbs(dot)) > 0;

                if (lastAntiClock != antiClock)
                {
                    // Pass zero.
                    if ((qAbs(m_lastArcDeg) < 90.) && (qAbs(degBetween) < 90.))
                    {
                        // Reset the move direction.
                        m_val = antiClock;
                    }
                    // Pass half.
                    else
                    {
                        degBetween = 360. - degBetween;
                    }
                }

                // Change the degree value wile the direction is moving anti-clockwisly.
                degBetween *= (m_val.toBool() ? 1. : -1);

                // Save the degree.
                m_lastArcDeg = degBetween;
            }   
        }

        vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
        transform->RotateWXYZ(degBetween / numSeg, axis[0], axis[1], axis[2]);
        transform->Translate(-orgPt[0], -orgPt[1], -orgPt[2]);

        // Insert the first point.
        m_points->InsertNextPoint(outPt);

        for (int i = 0; i < numSeg; i++)
        {
            double* newOutPos = transform->TransformDoublePoint(outPt);
            outPt[0] = newOutPos[0] + orgPt[0];
            outPt[1] = newOutPos[1] + orgPt[1];
            outPt[2] = newOutPos[2] + orgPt[2];
            m_points->InsertNextPoint(outPt);
        }

        // The arc( or full ) circle need one more cell for the last to the first point.
        int numPt = numSeg + 1;
        for (int i = 0; i < numSeg; i++)
        {
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(i);
            cell->InsertNextId((i + 1) % numPt);
            m_ugrid->InsertNextCell(VTKCellType::VTK_LINE, cell);
        }

        if (drawRadius)
        {
            int iPt = m_points->InsertNextPoint(orgPt);

            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(iPt - 1);
            cell->InsertNextId(iPt);
            m_ugrid->InsertNextCell(VTKCellType::VTK_LINE, cell);
        }

        // Pressed + Pressed + Pressed
        if (m_pressedPoses.count() == 3)
        {
            // Get the real end position of the arc.
            vtkSmartPointer<vtkTransform> transformForLast = vtkSmartPointer<vtkTransform>::New();
            transformForLast->RotateWXYZ(degBetween, axis[0], axis[1], axis[2]);
            transformForLast->Translate(-orgPt[0], -orgPt[1], -orgPt[2]);

            // Rotate from the start position of the arc.
            double* realLastPos = transformForLast->TransformDoublePoint(m_pressedPoses[1].GetData());
            realLastPos[0] += orgPt[0];
            realLastPos[1] += orgPt[1];
            realLastPos[2] += orgPt[2];

            // Replace the end position.
            m_pressedPoses.replace(2, vtkVector3d(realLastPos));

            // Deal with the clockwise direction.
            if (!m_val.toBool())
            {
                m_pressedPoses.swapItemsAt(1, 2);
            }

            return true;
        }

        return false;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawArcTan()
    {
        // Draw arc.
        QList<vtkVector3d> poses;
        bool flag = drawArcByCirclePoints(poses);
        if (!flag)
        {
            return false;
        }

        // Pressed + Previewed
        if (m_pressedPoses.count() == 1)
        {
            return false;
        }
        // Pressed + Pressed
        else if (m_pressedPoses.count() == 2)
        {
            // Deal with the positions.
            m_pressedPoses.clear();

            // Center, start, end ( anti-clockwise )
            m_pressedPoses = poses;

            return true;
        }

        return false;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawArcThreePoints()
    {
        // Draw arc.
        QList<vtkVector3d> poses;
        bool flag = drawArcByCirclePoints(poses);
        if (!flag)
        {
            return false;
        }

        // Pressed + Previewed / Pressed + Previewed
        if (m_pressedPoses.count() == 1 || m_pressedPoses.count() == 2)
        {
            return false;
        }
        // Pressed + Pressed + Pressed
        else if (m_pressedPoses.count() == 3)
        {
            // Deal with the positions.
            m_pressedPoses.clear();

            // Center, start, end ( anti-clockwise )
            m_pressedPoses = poses;

            return true;
        }

        return false;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawSpline()
    {
        if (m_pressedPoses.isEmpty())
        {
            return false;
        }

        QList<vtkVector3d> poses = m_pressedPoses + m_previewedPoses;
        if (poses.count() < 2)
        {
            return false;
        }

        // Create the spline function source.
        vtkSmartPointer<vtkParametricSpline> splineParam = vtkSmartPointer<vtkParametricSpline>::New();
        vtkSmartPointer<vtkParametricFunctionSource> funcSource = vtkSmartPointer<vtkParametricFunctionSource>::New();
        funcSource->SetParametricFunction(splineParam);
        splineParam->SetParameterizeByLength(false);

        // Add the path points.
        splineParam->SetNumberOfPoints(poses.count());
        int iPt = 0;
        for (const vtkVector3d & pos : poses)
        {
            splineParam->SetPoint(iPt, pos[0], pos[1], pos[2]);
            iPt++;
        }

        // Initialize spline parameters.
        //@{
        splineParam->SetLeftConstraint(3);
        splineParam->SetRightConstraint(1);

        splineParam->SetLeftValue(0.5);
        splineParam->SetRightValue(-0.5);
        //@}

        funcSource->Update();

        // Copy output.( After this function, the point must be set to the grid again. )
        m_ugrid->DeepCopy(funcSource->GetOutput());

        return false;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawConLine()
    {
        if (m_pressedPoses.isEmpty())
        {
            return false;
        }

        QList<vtkVector3d> poses = m_pressedPoses + m_previewedPoses;
        if (poses.count() < 2)
        {
            return false;
        }

        bool flag = drawLineByTwoPoints(poses);
        if (!flag)
        {
            return false;
        }

        // Pressed + Previewed
        if (m_pressedPoses.count() == 1)
        {
            return false;
        }
        // Pressed + Pressed
        else if (m_pressedPoses.count() >= 2)
        {
            m_pressedPoses = poses;
            return true;
        }

        return false;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawConHLine()
    {
        QList<vtkVector3d> poses = m_pressedPoses + m_previewedPoses;
        if (poses.count() < 1)
        {
            return false;
        }

        // Add a new position by right direction.
        vtkVector3d pos = poses.first();
        for (int i = 0; i < 3; i++)
        {
            pos[i] += m_planeRight[i] * 10;
        }

        poses.push_back(pos);

        bool flag = drawLineByTwoPoints(poses);
        if (!flag)
        {
            return false;
        }

        // Pressed
        if (m_pressedPoses.count() == 1)
        {
            // Add the second position.
            m_pressedPoses = poses;
            return true;
        }
        else
        {
            return false;
        }

        return false;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawConVLine()
    {
        QList<vtkVector3d> poses = m_pressedPoses + m_previewedPoses;
        if (poses.count() < 1)
        {
            return false;
        }

        // Add a new position by up direction.
        vtkVector3d pos = poses.first();
        for (int i = 0; i < 3; i++)
        {
            pos[i] += m_planeUp[i] * 10;
        }

        poses.push_back(pos);

        bool flag = drawLineByTwoPoints(poses);
        if (!flag)
        {
            return false;
        }

        // Pressed
        if (m_pressedPoses.count() == 1)
        {
            // Add the second position.
            m_pressedPoses = poses;
            return true;
        }
        else
        {
            return false;
        }

        return false;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawConAngleLine()
    {
        // Get the angle value.
        if (!m_shapeParam.isValid())
        {
            return false;
        }

        double angle = m_shapeParam.toDouble();

        QList<vtkVector3d> poses = m_pressedPoses + m_previewedPoses;
        if (poses.count() < 1)
        {
            return false;
        }

        // Get a new position by right direction.( horizontal )
        vtkVector3d pos = poses.first();
        vtkVector3d pos2H;
        for (int i = 0; i < 3; i++)
        {
            pos2H[i] = pos[i] + m_planeRight[i] * 10;
        }

        // Rotate the direction by the angle.
        vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
        transform->RotateWXYZ(angle, m_planeNor);        

        double* newOutPos = transform->TransformDoublePoint(
            m_planeRight[0] * m_size * 0.5, 
            m_planeRight[1] * m_size * 0.5,
            m_planeRight[2] * m_size * 0.5);
        for (int i = 0; i < 3; i++)
        {
            newOutPos[i] += pos[i];
        }

        poses.push_back(vtkVector3d(newOutPos));

        bool flag = drawLineByTwoPoints(poses);
        if (!flag)
        {
            return false;
        }

        // Pressed
        if (m_pressedPoses.count() == 1)
        {
            // Add the second position.
            m_pressedPoses = poses;
            return true;
        }
        else
        {
            return false;
        }

        return false;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawConCircle()
    {
        if (m_pressedPoses.isEmpty())
        {
            return false;
        }

        QList<vtkVector3d> poses = m_pressedPoses + m_previewedPoses;
        if (poses.count() < 2)
        {
            return false;
        }

        double orgPt[3]{ poses[0][0], poses[0][1], poses[0][2] };
        double outPt[3]{ poses[1][0], poses[1][1], poses[1][2] };
        vtkVector3d axis(m_planeNor);

        int numPts = 30;
        double degStep = 360. / numPts;

        vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
        transform->RotateWXYZ(degStep, axis[0], axis[1], axis[2]);
        transform->Translate(-orgPt[0], -orgPt[1], -orgPt[2]);

        for (int i = 0; i < numPts; i++)
        {
            double* newOutPos = transform->TransformDoublePoint(outPt);
            outPt[0] = newOutPos[0] + orgPt[0];
            outPt[1] = newOutPos[1] + orgPt[1];
            outPt[2] = newOutPos[2] + orgPt[2];
            m_points->InsertNextPoint(outPt);
        }

        for (int i = 0; i < numPts; i++)
        {
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(i);
            cell->InsertNextId((i + 1) % numPts);
            m_ugrid->InsertNextCell(VTKCellType::VTK_LINE, cell);
        }

        // Pressed + Previewed
        if (m_pressedPoses.count() == 1)
        {
            return false;
        }
        // Pressed + Pressed
        else if (m_pressedPoses.count() >= 2)
        {
            return true;
        }

        return false;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawArcByCirclePoints(QList<vtkVector3d> & oPoses)
    {
        if (m_pressedPoses.isEmpty())
        {
            return false;
        }

        QList<vtkVector3d> poses = m_pressedPoses + m_previewedPoses;
        if (poses.count() < 2)
        {
            return false;
        }

        // Get the start and end point of the arc.
        vtkVector3d startPt(poses[0][0], poses[0][1], poses[0][2]);
        vtkVector3d endPt(poses[1][0], poses[1][1], poses[1][2]);

        vtkVector3d midPt = vtkVector3d((endPt[0] + startPt[0]) * 0.5, (endPt[1] + startPt[1]) * 0.5,
            (endPt[2] + startPt[2]) * 0.5);
        vtkVector3d vecStartToEnd = vtkVector3d(endPt[0] - startPt[0], endPt[1] - startPt[1], 
            endPt[2] - startPt[2]).Normalized();

        // Get the degrees with the project vector and the plane direction.
        double degRight = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(m_planeRight, vecStartToEnd.GetData()));
        double degUp = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(m_planeUp, vecStartToEnd.GetData()));

        double vR = degRight > 90. ? -1. : 1;
        double vU = degUp > 90. ? -1. : 1;

        vtkVector3d vecNor(m_planeNor);
        vtkVector3d vecUp(m_planeUp);

        double degBetween = 0.;
        vtkVector3d center;

        // Two points arc.
        if (poses.count() == 2)
        {
            // Draw a full circle with the plane position.
            if (startPt == endPt)
            {
                center = vtkVector3d(m_planePos);
                degBetween = 360.;
            }
            else
            {
                // Check the relative position between the second position and the first position.
                // Cross any of the directions with the direction of two points.
                // For the right and left. ( half circle )
                double dirCheck[3];
                vtkMath::Cross(vecStartToEnd.GetData(), m_planeRight, dirCheck);
                if (qAbs(vtkMath::Norm(dirCheck)) < 1e-6)
                {
                    degBetween = 180.;

                    // The center is the mid point of start and end.
                    center = midPt;

                    // And the start and the end need to be reversed.
                    endPt = vtkVector3d(poses[0][0], poses[0][1], poses[0][2]);
                    startPt = vtkVector3d(poses[1][0], poses[1][1], poses[1][2]);
                }
                else
                {
                    // Get the median line of the start point and the end point.
                    //@{
                    vtkVector3d vecMedian = vecStartToEnd.Cross(vecNor);
                    if (FITKGraphCommons::FuzzyCompare(vecMedian.Normalize(), 0))
                    {
                        return false;
                    }
                    //@}

                    // The start point will draw a circle to the right.( Use the plane's up direction. )
                    // Get the cross point.( The center of the circle )
                    double p2[3]{ startPt[0] + m_planeUp[0], startPt[1] + m_planeUp[1], startPt[2] + m_planeUp[2] };
                    double p4[3]{ midPt[0] + vecMedian[0], midPt[1] + vecMedian[1], midPt[2] + vecMedian[2] };
                    double t;

                    double cross3[3]{ 0., 0., 0. };
                    bool hasCross = getCrossPoint(startPt.GetData(), p2, midPt.GetData(), p4, cross3, t);
                    Q_UNUSED(hasCross);
                    center = vtkVector3d(cross3);

                    //vtkLine::Intersection(startPt.GetData(), p2, midPt.GetData(), p4, t, u);
                    //center = vtkVector3d(startPt[0] + t * vecUp[0], startPt[1] + t * vecUp[1], startPt[2] + t * vecUp[2]);

                    // Get the angle with the start position to the center and the mouse position to the center.
                    vtkVector3d vecStartToCenter(startPt[0] - center[0], startPt[1] - center[1], startPt[2] - center[2]);
                    vtkVector3d vecEndToCenter(endPt[0] - center[0], endPt[1] - center[1], endPt[2] - center[2]);
                    degBetween = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(vecStartToCenter.GetData(), vecEndToCenter.GetData()));

                    // Reverse.
                    if (vR > 0 && vU > 0)
                    {
                        // Break.
                    }
                    else if (vR < 0 && vU < 0)
                    {
                        degBetween = 360. - degBetween;

                        endPt = vtkVector3d(poses[0][0], poses[0][1], poses[0][2]);
                        startPt = vtkVector3d(poses[1][0], poses[1][1], poses[1][2]);
                    }
                    else if (vR > 0 && vU < 0)
                    {
                        endPt = vtkVector3d(poses[0][0], poses[0][1], poses[0][2]);
                        startPt = vtkVector3d(poses[1][0], poses[1][1], poses[1][2]);
                    }
                    else if (vR < 0 && vU > 0)
                    {
                        degBetween = 360. - degBetween;
                    }
                }
            }
        }
        // Three points arc.
        else if (poses.count() == 3)
        {
            vtkVector3d lastPt(poses[2][0], poses[2][1], poses[2][2]);
            vtkVector3d vecStartToLast(lastPt[0] - startPt[0], lastPt[1] - startPt[1], lastPt[2] - startPt[2]);
            vtkVector3d vecEndToLast(lastPt[0] - endPt[0], lastPt[1] - endPt[1], lastPt[2] - endPt[2]);

            // Check if the three points are in the same line.
            vtkVector3d v1;
            vtkVector3d v2;
            for (int i = 0; i < 3; i++)
            {
                v1[i] = poses[1][i] - poses[0][i];
                v2[i] = poses[2][i] - poses[1][i];
            }

            vtkVector3d newVec = v1.Cross(v2);
            if (FITKGraphCommons::FuzzyCompare(0, newVec.Norm()))
            {
                // Clear the dataset.
                resetVtkObj(m_points);
                resetVtkObj(m_ugrid);
                m_ugrid->SetPoints(m_points);

                return false;
            }

            // Get the median line.
            //@{
            // From start point to the end point.
            vtkVector3d vecMedianSE = vecStartToEnd.Cross(vecNor);
            if (FITKGraphCommons::FuzzyCompare(vecMedianSE.Normalize(), 0))
            {
                return false;
            }

            // From start point to the last point.
            vtkVector3d vecMedianSL = vecStartToLast.Cross(vecNor);
            if (FITKGraphCommons::FuzzyCompare(vecMedianSL.Normalize(), 0))
            {
                return false;
            }
            //@}

            // Get the cross point of the two median lines.( The center of the circle )
            double p2[3]{ midPt[0] + vecMedianSE[0], midPt[1] + vecMedianSE[1], midPt[2] + vecMedianSE[2] };
            double p3[3]{ (poses[0][0] + poses[2][0]) * 0.5, (poses[0][1] + poses[2][1]) * 0.5, 
                (poses[0][2] + poses[2][2]) * 0.5 };
            double p4[3]{ p3[0] + vecMedianSL[0], p3[1] + vecMedianSL[1], p3[2] + vecMedianSL[2] };
            double t, u;
            vtkLine::Intersection(midPt.GetData(), p2, p3, p4, t, u);
            center = vtkVector3d(midPt[0] + t * vecMedianSE[0], midPt[1] + t * vecMedianSE[1], midPt[2] + t * vecMedianSE[2]);
        
            // Get the angle with the start position to the center and the end position to the center.
            vtkVector3d vecStartToCenter(startPt[0] - center[0], startPt[1] - center[1], startPt[2] - center[2]);
            vtkVector3d vecEndToCenter(endPt[0] - center[0], endPt[1] - center[1], endPt[2] - center[2]);
            degBetween = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(vecStartToCenter.GetData(), vecEndToCenter.GetData()));

            // Get the median of the start and end as the y axis.
            // Get the last position's y position with the median.
            double projLastToYAxis[3];
            vtkMath::ProjectVector(vecStartToLast.GetData(), vecMedianSE.GetData(), projLastToYAxis);

            // And then get the x direction.
            vtkVector3d vecXAxis = vecMedianSE.Cross(vecNor).Normalized();

            // Get the degree between the first to last and the end to last.
            double deg = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(vecStartToLast.GetData(), vecEndToLast.GetData()));

            // Get the direction of mid point of the start and end to the last.
            vtkVector3d vecMidToLast = vtkVector3d(lastPt[0] - midPt[0], lastPt[1] - midPt[1], lastPt[2] - midPt[2]);

            // Get the degree between the median of the first and end to last and the mid to last.
            double degScale = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(vecMedianSE.GetData(), vecMidToLast.GetData()));

            // Reverse.
            if (deg < 90.)
            {
                degBetween = 360. - degBetween;
            }

            if (degScale > 90.)
            {
                endPt = vtkVector3d(poses[0][0], poses[0][1], poses[0][2]);
                startPt = vtkVector3d(poses[1][0], poses[1][1], poses[1][2]);
            }
        }

        // Draw arc.
        int numSeg = 60;
        vtkVector3d axis(m_planeNor);

        vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
        transform->RotateWXYZ(degBetween / numSeg, axis[0], axis[1], axis[2]);
        transform->Translate(-center[0], -center[1], -center[2]);

        // Insert the first point.
        double pos3[3]{ startPt[0], startPt[1], startPt[2] };
        m_points->InsertNextPoint(pos3);

        for (int i = 0; i < numSeg; i++)
        {
            double* newOutPos = transform->TransformDoublePoint(pos3);
            pos3[0] = newOutPos[0] + center[0];
            pos3[1] = newOutPos[1] + center[1];
            pos3[2] = newOutPos[2] + center[2];
            m_points->InsertNextPoint(pos3);
        }

        // The arc( or full ) circle need one more cell for the last to the first point.
        int numPt = numSeg + 1;
        for (int i = 0; i < numSeg; i++)
        {
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(i);
            cell->InsertNextId((i + 1) % numPt);
            m_ugrid->InsertNextCell(VTKCellType::VTK_LINE, cell);
        }

        // Save the positions. Center, start, end ( anti-clockwise )
        oPoses.clear();
        oPoses.push_back(vtkVector3d(center[0], center[1], center[2]));
        oPoses.push_back(vtkVector3d(startPt[0], startPt[1], startPt[2]));
        oPoses.push_back(vtkVector3d(endPt[0], endPt[1], endPt[2]));

        return true;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::drawLineByTwoPoints(QList<vtkVector3d> & ioPoses)
    {
        if (ioPoses.count() != 2)
        {
            return false;
        }

        // Get four cornor positions.
        double posTL[3], posTR[3], posBL[3], posBR[3];
        for (int i = 0; i < 3; i++)
        {
            posTL[i] = m_planePos[i] + m_size * 0.5 * m_planeUp[i] - m_size * 0.5 * m_planeRight[i];
            posTR[i] = m_planePos[i] + m_size * 0.5 * m_planeUp[i] + m_size * 0.5 * m_planeRight[i];
            posBL[i] = m_planePos[i] - m_size * 0.5 * m_planeUp[i] - m_size * 0.5 * m_planeRight[i];
            posBR[i] = m_planePos[i] - m_size * 0.5 * m_planeUp[i] + m_size * 0.5 * m_planeRight[i];
        }

        // Get the four cross positions.
        double tT, tR, tB, tL;
        double pt_1[3]{ ioPoses.first()[0], ioPoses.first()[1], ioPoses.first()[2] };
        double pt_2[3]{ ioPoses.last()[0], ioPoses.last()[1], ioPoses.last()[2] };

        double cPtT[3], cPtR[3], cPtB[3], cPtL[3];
        getCrossPoint(posTL, posTR, pt_1, pt_2, cPtT, tT);
        getCrossPoint(posTR, posBR, pt_1, pt_2, cPtR, tR);
        getCrossPoint(posBR, posBL, pt_1, pt_2, cPtB, tB);
        getCrossPoint(posBL, posTL, pt_1, pt_2, cPtL, tL);

        QList<vtkVector3d> poses;

        // Lambda for adding new position.
        auto addPos = [&](QList<vtkVector3d> & poses, vtkVector3d pos)
        {
            if (!poses.contains(pos))
            {
                poses.push_back(pos);
            }
        };

        if (!qIsNaN(tT) && tT > 0. && tT < 1.)
        {
            addPos(poses, vtkVector3d(cPtT));
        }

        if (!qIsNaN(tR) && tR > 0. && tR < 1.)
        {
            addPos(poses, vtkVector3d(cPtR));
        }

        if (!qIsNaN(tB) && tB > 0. && tB < 1.)
        {
            addPos(poses, vtkVector3d(cPtB));
        }

        if (!qIsNaN(tL) && tL > 0. && tL < 1.)
        {
            addPos(poses, vtkVector3d(cPtL));
        }

        // Out of the grid range.
        if (poses.count() == 0)
        {
            addPos(poses, vtkVector3d(pt_1));
            addPos(poses, vtkVector3d(pt_2));
        }

        // Returned the new positions.
        ioPoses.clear();

        // Up -> Right.
        double firstToSecond[3]{ pt_2[0] - pt_1[0], pt_2[1] - pt_1[1], pt_2[2] - pt_1[2] };
        vtkMath::Normalize(firstToSecond);
        double degUp = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(m_planeUp, firstToSecond));
        double degRight = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(m_planeRight, firstToSecond));    

        // Use up or right direction.
        if (Exchange::FITKGraphCommons::FuzzyCompare(degUp, 90.))
        {
            // Left to right.
            ioPoses.push_back(vtkVector3d(cPtL));
            ioPoses.push_back(vtkVector3d(cPtR));
        }
        else if (degUp > 90.)
        {
            if (Exchange::FITKGraphCommons::FuzzyCompare(degUp, 180.))
            {
                ioPoses.push_back(vtkVector3d(cPtB));
                ioPoses.push_back(vtkVector3d(cPtT));
            }
            else
            {
                ioPoses.push_back(poses.last());
                ioPoses.push_back(poses.first());
            }
        }
        else
        {
            if (Exchange::FITKGraphCommons::FuzzyCompare(degUp, 0.))
            {
                ioPoses.push_back(vtkVector3d(cPtB));
                ioPoses.push_back(vtkVector3d(cPtT));
            }
            else
            {
                ioPoses.push_back(poses.first());
                ioPoses.push_back(poses.last());
            }
        }

        // Create dataset.
        m_points->InsertNextPoint(ioPoses.first().GetData());
        m_points->InsertNextPoint(ioPoses.last().GetData());

        vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
        cell->InsertNextId(0);
        cell->InsertNextId(1);
        m_ugrid->InsertNextCell(VTKCellType::VTK_LINE, cell);

        return true;
    }

    bool FITKGraphObjectSketchInteractivePreviewer::getCrossPoint(double* iP1, double* iP2, double* iP3, double* iP4, double* oPx, double & oT1)
    {
        double d1[3], d2[3];
        vtkMath::Subtract(iP2, iP1, d1);
        vtkMath::Subtract(iP4, iP3, d2);

        double cross[3];
        vtkMath::Cross(d1, d2, cross);

        // Parallel.
        if (qAbs(vtkMath::Norm(cross)) < 1e-6) 
        {
            oT1 = qQNaN();
            return false;
        }

        double diff[3];
        vtkMath::Subtract(iP3, iP1, diff);

        double cross1[3], cross2[3];
        vtkMath::Cross(diff, d2, cross1);
        vtkMath::Cross(d1, d2, cross2);

        double t = vtkMath::Dot(cross1, cross2) / vtkMath::Dot(cross2, cross2);

        for (int i = 0; i < 3; ++i)
        {
            oPx[i] = iP1[i] + t * d1[i];
        }

        oT1 = t;

        return true;
    }
}   // namespace Exchange